package com.stable.service;

import com.alibaba.fastjson.JSON;
import com.google.common.util.concurrent.ListenableFuture;
import com.stable.constant.RedisConstant;
import com.stable.enums.RunCycleEnum;
import com.stable.enums.RunLogBizTypeEnum;
import com.stable.es.dao.base.EsNewFinanceDao;
import com.stable.job.MyCallable;
import com.stable.spider.tushare.TushareSpider;
import com.stable.utils.RedisUtil;
import com.stable.utils.TasksWorker;
import com.stable.vo.bus.FiveYearFinanceReturnInfo;
import com.stable.vo.bus.NewFinance;
import com.stable.vo.bus.StockBaseInfo;
import com.stable.vo.spi.req.EsQueryPageReq;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * 财务报表
 *
 * @author zhouwen
 */
@Service
@Log4j2
public class FiveYearFinanceService {
    @Autowired
    TushareSpider tushareSpider;
    @Autowired
    private EsNewFinanceDao esNewFinanceDao;
    @Autowired
    private RedisUtil redisUtil;
    private final Semaphore semapProBar = new Semaphore(1);


    // 查询
    public List<FiveYearFinanceReturnInfo> queryFinance(String queryDate, String code, double last5YearRoeAllPercent, double last5YearRoeAveragePercent, double last1YearYsPercent, double last3YearYsPercent, double last1YearLrPercent, double last3YearLrPercent, EsQueryPageReq queryPage, String conceptName, String gsld,
                                                        Integer ysLrPassCount, Boolean before10RoePlus) {
        log.info("queryFinance queryDate={},last5YearRoeAllPercent={},last5YearRoeAveragePercent={},last1YearYsPercent={},last3YearYsPercent={},last1YearLrPercent={},last3YearLrPercent={},pageable.page={},pageable.size={}, sortName:{} ,sortOrder:{}"
                , queryDate, last5YearRoeAllPercent, last5YearRoeAveragePercent, last1YearYsPercent, last3YearYsPercent, last1YearLrPercent, last3YearLrPercent, queryPage.getPageNum(),
                queryPage.getPageSize(), queryPage.getSortName(), queryPage.getSortOrder());

        String redisMapKey = "fiveYearFinance_" + queryDate;
        List<FiveYearFinanceReturnInfo> infos = new ArrayList<>();
        Map<Object, Object> objectMap = redisUtil.hGetAll(redisMapKey);
        if (objectMap.size() == 0) {
            jobQueryFinanceToRedis(queryDate, code, last5YearRoeAllPercent, last5YearRoeAveragePercent, last1YearYsPercent, last3YearYsPercent, last1YearLrPercent, last3YearLrPercent, queryPage);
            return null;
        } else {
            objectMap.values().stream().forEach(info -> {
                FiveYearFinanceReturnInfo newFinanceReturnInfo = JSON.parseObject((String) info, FiveYearFinanceReturnInfo.class);
//                StockBaseInfo stockBaseInfo = JSON.parseObject((String) stockBasicMap.get(newFinanceReturnInfo.getCode()), StockBaseInfo.class);
//                if (!Objects.isNull(stockBaseInfo)) {
//                    newFinanceReturnInfo.setName(stockBaseInfo.getName());
//                    newFinanceReturnInfo.setConceptName(stockBaseInfo.getConcept_name());
//                    newFinanceReturnInfo.setGsld(stockBaseInfo.getGsld());
//                }
                if (StringUtils.isNotBlank(conceptName)) {
                    if (StringUtils.isNotBlank(newFinanceReturnInfo.getConceptName()) && !newFinanceReturnInfo.getConceptName().contains(conceptName))
                        return;
                }
                if (StringUtils.isNotBlank(gsld)) {
                    if (StringUtils.isNotBlank(newFinanceReturnInfo.getGsld()) && !newFinanceReturnInfo.getGsld().contains(gsld))
                        return;
                }
                if (newFinanceReturnInfo.getYsLrPassCount() < ysLrPassCount) return;
                if (before10RoePlus) {
                    if (newFinanceReturnInfo.isBefore10RoePlus() == false) return;
                }
                infos.add(newFinanceReturnInfo);
            });
            return infos;
        }
    }


    // 把水平涨跌弧度数据写入redis
    public ListenableFuture<Object> jobQueryFinanceToRedis(String queryDate, String code, double last5YearRoeAllPercent, double last5YearRoeAveragePercent, double last1YearYsPercent, double last3YearYsPercent, double last1YearLrPercent, double last3YearLrPercent, EsQueryPageReq queryPage) {

        if (queryPage.getPageSize() == 0) queryPage.setPageSize(1000); //默认取1只股票3年数据
        log.info("page startDate={},pageable.page={},pageable.size={}, sortName:{} ,sortOrder:{}", queryPage.getPageNum(),
                queryPage.getPageSize(), queryPage.getSortName(), queryPage.getSortOrder());
        return TasksWorker.getInstance().getService()
                .submit(new MyCallable(RunLogBizTypeEnum.FIVE_FINANCE_ANALYZE, RunCycleEnum.DAY) {
                    public Object mycall() {
                        return doFinanceMethod(queryDate, code, last5YearRoeAllPercent, last5YearRoeAveragePercent, last1YearYsPercent, last3YearYsPercent, last1YearLrPercent, last3YearLrPercent, queryPage);
                    }
                });
    }

    public Object doFinanceMethod(String queryDate, String code, double last5YearRoeAllPercent, double last5YearRoeAveragePercent, double last1YearYsPercent, double last3YearYsPercent, double last1YearLrPercent, double last3YearLrPercent, EsQueryPageReq queryPage) {
        try {
            semapProBar.acquire();
            String redisMapKey = "fiveYearFinance_" + queryDate;

            Map<Object, Object> stockBasicMap = redisUtil.hGetAll(RedisConstant.stock_basic);
            if (StringUtils.isNotBlank(code)) {
                StockBaseInfo stockBaseInfo = JSON.parseObject((String) stockBasicMap.get(code), StockBaseInfo.class);
                if (!Objects.isNull(stockBaseInfo)) {
                    doCodeAnalyMethod(queryDate, code, last5YearRoeAllPercent, last5YearRoeAveragePercent, last1YearYsPercent, last3YearYsPercent, last1YearLrPercent, last3YearLrPercent, queryPage, redisMapKey, stockBaseInfo);
                }
            } else {
                stockBasicMap.values().stream().forEach(info -> {
                    StockBaseInfo stockBaseInfo = JSON.parseObject((String) info, StockBaseInfo.class);
                    if (!Objects.isNull(stockBaseInfo)) {
                        doCodeAnalyMethod(queryDate, stockBaseInfo.getCode(), last5YearRoeAllPercent, last5YearRoeAveragePercent, last1YearYsPercent, last3YearYsPercent, last1YearLrPercent, last3YearLrPercent, queryPage, redisMapKey, stockBaseInfo);
                    }
                });
            }
            redisUtil.expire(redisMapKey, 10, TimeUnit.DAYS);
            return null;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return null;
        } finally {
            semapProBar.release();
        }
    }

    private boolean doCodeAnalyMethod(String queryDate, String code, double last5YearRoeAllPercent, double last5YearRoeAveragePercent, double last1YearYsPercent, double last3YearYsPercent, double last1YearLrPercent, double last3YearLrPercent, EsQueryPageReq queryPage, String redisMapKey, StockBaseInfo stockBaseInfo) {
        // 年报数据
        List<NewFinance> financeYearInfos = getNewFinancesEs(code, queryPage, 1);
        if (financeYearInfos.size() <= 5) return true;
        FiveYearFinanceReturnInfo financeReturnInfo = new FiveYearFinanceReturnInfo();
        for (int i = 1; i <= 9; i++) {
            if (financeYearInfos.size() < i) continue;
            NewFinance newFinance = financeYearInfos.get(i - 1);
            double zcfzl = newFinance.getZcfzl();   //资产负债率
            // 最近1年的财务指标过滤
            if (i == 1) {
                double yyzsrtbzz = newFinance.getYyzsrtbzz();   //最近1次的营业总收入同比增长(%) >= 入参
                double jqjzcsyl = newFinance.getJqjzcsyl();     //最近1次的加权净资产收益率(%) ROE >= 入参
                double gsjlrtbzz = newFinance.getGsjlrtbzz();   //最近1次的归属净利润同比增长(%) > 0
                double kfjlrtbzz = newFinance.getKfjlrtbzz();   //最近1次扣非净利润同比增长(%)
                financeReturnInfo.setLast1YyzsrtbzzPercent(yyzsrtbzz);
                financeReturnInfo.setLast1Gsjlrtbzz(gsjlrtbzz);
                financeReturnInfo.setLast1Kfjlrtbzz(kfjlrtbzz);
                financeReturnInfo.setLast1JqjzcsylPercent(jqjzcsyl);
            }

            // 最近第2年的财务指标过滤
            if (i == 2) {
                double yyzsrtbzz = newFinance.getYyzsrtbzz();
                double jqjzcsyl = newFinance.getJqjzcsyl();
                double gsjlrtbzz = newFinance.getGsjlrtbzz();
                double kfjlrtbzz = newFinance.getKfjlrtbzz();

                financeReturnInfo.setLast2YyzsrtbzzPercent(yyzsrtbzz);
                financeReturnInfo.setLast2Gsjlrtbzz(gsjlrtbzz);
                financeReturnInfo.setLast2Kfjlrtbzz(kfjlrtbzz);
                financeReturnInfo.setLast2JqjzcsylPercent(jqjzcsyl);
            }


            // 最近第3年的财务指标过滤
            if (i == 3) {
                double yyzsrtbzz = newFinance.getYyzsrtbzz();
                double jqjzcsyl = newFinance.getJqjzcsyl();
                double gsjlrtbzz = newFinance.getGsjlrtbzz();
                double kfjlrtbzz = newFinance.getKfjlrtbzz();

                financeReturnInfo.setLast3YyzsrtbzzPercent(yyzsrtbzz);
                financeReturnInfo.setLast3Gsjlrtbzz(gsjlrtbzz);
                financeReturnInfo.setLast3Kfjlrtbzz(kfjlrtbzz);
                financeReturnInfo.setLast3JqjzcsylPercent(jqjzcsyl);
            }

            // 最近第4年的财务指标过滤
            if (i == 4) {
                double yyzsrtbzz = newFinance.getYyzsrtbzz();
                double jqjzcsyl = newFinance.getJqjzcsyl();
                double gsjlrtbzz = newFinance.getGsjlrtbzz();
                double kfjlrtbzz = newFinance.getKfjlrtbzz();

                financeReturnInfo.setLast4YyzsrtbzzPercent(yyzsrtbzz);
                financeReturnInfo.setLast4Gsjlrtbzz(gsjlrtbzz);
                financeReturnInfo.setLast4Kfjlrtbzz(kfjlrtbzz);
                financeReturnInfo.setLast4JqjzcsylPercent(jqjzcsyl);
            }

            // 最近第5年的财务指标过滤
            if (i == 5) {
                double yyzsrtbzz = newFinance.getYyzsrtbzz();
                double jqjzcsyl = newFinance.getJqjzcsyl();
                double gsjlrtbzz = newFinance.getGsjlrtbzz();
                double kfjlrtbzz = newFinance.getKfjlrtbzz();

                financeReturnInfo.setLast5YyzsrtbzzPercent(yyzsrtbzz);
                financeReturnInfo.setLast5Gsjlrtbzz(gsjlrtbzz);
                financeReturnInfo.setLast5Kfjlrtbzz(kfjlrtbzz);
                financeReturnInfo.setLast5JqjzcsylPercent(jqjzcsyl);
            }

            // 最近第10年的财务指标过滤
            if (i == 9) {
                double jqjzcsyl = newFinance.getJqjzcsyl();
                //10年前ROE为正
                if (jqjzcsyl >= 0) {
                    financeReturnInfo.setBefore10RoePlus(true);
                }
            }
        }


        //ROE
        double last1Roe = financeReturnInfo.getLast1JqjzcsylPercent();
        double last2Roe = financeReturnInfo.getLast2JqjzcsylPercent();
        double last3Roe = financeReturnInfo.getLast3JqjzcsylPercent();
        double last4Roe = financeReturnInfo.getLast4JqjzcsylPercent();
        double last5Roe = financeReturnInfo.getLast5JqjzcsylPercent();

        //营收增速
        double last1Ys = financeReturnInfo.getLast1YyzsrtbzzPercent();
        double last2Ys = financeReturnInfo.getLast2YyzsrtbzzPercent();
        double last3Ys = financeReturnInfo.getLast3YyzsrtbzzPercent();
        //利润增速
        double last1Lr = financeReturnInfo.getLast1Kfjlrtbzz();
        double last2Lr = financeReturnInfo.getLast2Kfjlrtbzz();
        double last3Lr = financeReturnInfo.getLast3Kfjlrtbzz();


        //double fiveYearAvgRoe = (last1Roe + last2Roe + last3Roe + last4Roe + last5Roe) / 5; //过去5年年化的平均增速
        //double threeYearYs = last1Ys + last2Ys + last3Ys; // 过去3年营收增速
        //double threeYearLr = last1Lr + last2Lr + last3Lr; //过去3年利润增速
        double fiveYearAvgRoe = (Math.pow((1 + last1Roe / 100) * (1 + last2Roe / 100) * (1 + last3Roe / 100) * (1 + last4Roe / 100) * (1 + last5Roe / 100), 0.2) - 1) * 100;
        double threeYearYs = (1 + last1Ys / 100) * (1 + last2Ys / 100) * (1 + last3Ys / 100) * 100 - 100; // 过去3年营收增速
        double threeYearLr = (1 + last1Lr / 100) * (1 + last2Lr / 100) * (1 + last3Lr / 100) * 100 - 100; //过去3年利润增速

        int passCount = 0;
        //ROE 过去5年都高于 10%
        if (last1Roe >= last5YearRoeAllPercent && last2Roe >= last5YearRoeAllPercent && last3Roe >= last5YearRoeAllPercent
                && last4Roe >= last5YearRoeAllPercent && last5Roe >= last5YearRoeAllPercent &&
                // 过去5年年化高于15%
                fiveYearAvgRoe >= last5YearRoeAveragePercent) {
            if (last1Ys >= last1YearYsPercent) passCount++; //过去1年营收增速>30%
            if (last1Lr >= last1YearLrPercent) passCount++; //过去1年利润增速>30%
            if (threeYearYs >= last3YearYsPercent) passCount++; //过去3年营收增速>100%
            if (threeYearLr >= last3YearLrPercent) passCount++; //过去3年利润增速>100%
            if (passCount >= 3) { //同时满足4个中的3个即可

            } else {
                return false;
            }
        } else {
            return false;
        }

        financeReturnInfo.setCode(stockBaseInfo.getCode());
        financeReturnInfo.setConceptName(stockBaseInfo.getConcept_name());
        financeReturnInfo.setName(stockBaseInfo.getName());
        financeReturnInfo.setGsld(stockBaseInfo.getGsld());
        financeReturnInfo.setArea(stockBaseInfo.getArea());
        financeReturnInfo.setIndustry(stockBaseInfo.getIndustry());
        financeReturnInfo.setZxjj(stockBaseInfo.getZxjj());
        financeReturnInfo.setZybl(stockBaseInfo.getZybl());
        financeReturnInfo.setSshy(stockBaseInfo.getSshy());
        financeReturnInfo.setYsLrPassCount(passCount);

//        // 按报告期（季度）财务数据
//        List<NewFinance> financeRepInfos = getNewFinancesEs(code, queryPage, 0);
//        int repSize = 3;
//        if (financeRepInfos.size() >= 4) repSize = 4;
//        for (int i = 1; i <= repSize; i++) {
//            NewFinance newFinance = financeRepInfos.get(i - 1);
//            // 最近（季度）的财务指标过滤，营业总收入同比增长、归属净利润同比增长必须是正数，资产负债率<60
//            double yyzsrtbzz = newFinance.getYyzsrtbzz();
//            double gsjlrtbzz = newFinance.getGsjlrtbzz();
//            double zcfzl = newFinance.getZcfzl();
//            if (yyzsrtbzz < 0 || gsjlrtbzz < 0 || zcfzl >= paramZcfzl) {
//                return false;
//            }
//        }
        log.info("添加redismap值,key:{},code:{}", redisMapKey, code);
        redisUtil.hPut(redisMapKey, code, JSON.toJSONString(financeReturnInfo));
        return true;
    }

    private List<NewFinance> getNewFinancesEs(String code, EsQueryPageReq queryPage, int type) {
        Pageable pageable = PageRequest.of(queryPage.getPageNum(), queryPage.getPageSize());
        BoolQueryBuilder bqb = QueryBuilders.boolQuery();
        if (StringUtils.isNotBlank(code)) {
            bqb.must(QueryBuilders.matchPhraseQuery("code", code));
        }
        bqb.filter(QueryBuilders.termQuery("type", type));
        FieldSortBuilder sort = SortBuilders.fieldSort(StringUtils.isEmpty(queryPage.getSortName()) ? "dateMilli" : queryPage.getSortName()).unmappedType("integer").order(SortOrder.DESC);

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        SearchQuery sq = queryBuilder.withQuery(bqb).withSort(sort).withPageable(pageable).build();
        return esNewFinanceDao.search(sq).getContent();
    }
}
